package com.atguigu.gulimall.search.thread;

import java.util.concurrent.*;

public class TestThread {
    // 在一个系统中最好创建1~2个线程池，所有的业务都共用该线程池
    static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("==================开始业务==================");
        /**
         * 1. 继承Thread类
         * Thread01 thread01 = new Thread01();
         * thread01.start();
         *
         * 2. 实现Runnable的run方法
         *         Runnable01 runnable01 = new Runnable01();
         *         new Thread(runnable01).start();
         *
         * 3. 实现Callable的call方法 + FutureTask
         *         FutureTask<Integer> task = new FutureTask<>(new Callable01());
         *         new Thread(task).start();
         *
         *         Integer result = task.get();
         *         System.out.println("业务返回结果：" + result);
         * 4. 线程池
         *         Future<Integer> future = executorService.submit(new Callable01());
         *         Integer result = future.get();
         *         System.out.println("业务返回结果：" + result);
         * 4.1 创建线程池
         * 4.1.1 通过Executors创建线程池
         * newFixedThreadPool(int nThread)          设置核心线程数
         * newScheduledThreadPool(int corePoolSize) 定时启动线程
         * newSingleThreadExecutor()                只创建一个核心线程，按照阻塞的形式依次执行任务
         * ......
         * 4.1.2 通过创建ThreadPoolExecutor对象创建线程池
         * ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 200, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
         *     public ThreadPoolExecutor(int corePoolSize,                      核心线程数：核心线程永远不会被释放，所有任务都先由核心线程处理，除非设置了allowCoreThreadTimeOut，核心线程才会被释放
         *                               int maximumPoolSize,                   连接池最大线程数：连接池的最大线程，当核心线程占满后，任务由其他线程处理
         *                               long keepAliveTime,                    线程存活时间：非核心线程若长时间未启用，则会被自动释放
         *                               TimeUnit unit,                         时间单位：线程存活时间的单位
         *                               BlockingQueue<Runnable> workQueue,     阻塞队列：当所有线程都被占用后，多出来的任务由阻塞队列处理，等待线程释放
         *                               ThreadFactory threadFactory,           线程工厂：线程创建的工厂
         *                               RejectedExecutionHandler handler)      拒绝策略：如果队列也满了，按照指定的拒绝策略拒绝任务
         * ThreadPoolExecutor执行过程
         * （1）所有任务优先由核心线程处理
         * （2）当核心线程占满后，先存放入阻塞队列中，当核心线程空闲后会自动到阻塞队列中获取任务
         * （3）当阻塞队列占满后，会根据线程池最大线程数开启新的线程，处理任务
         * （4）当最大线程数也占满后，会根据拒绝策略拒绝任务
         * （5）当非核心线程有空闲时，会根据存活时间，自动释放线程
         *
         * 注意：LinkedBlockingDeque在创建队列时，会按Integer的最大值创建，为避免内存溢出，还需要根据实际情况设置最大队列
         *
         * 5. 使用CompletableFuture实现异步任务编排
         * 5.1 runAsync supplyAsync
         *         CompletableFuture.runAsync(() -> {
         *             System.out.println("当前线程ID：" + Thread.currentThread().getId());
         *             int i = 10/2;
         *             System.out.println("线程运行结果为：" + i);
         *         }, executorService);
         * 调用runAsync，需要传入一个函数表达式和线程池，没有返回值；
         *
         *         CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
         *             System.out.println("当前线程ID：" + Thread.currentThread().getId());
         *             int i = 10 / 2;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         * 调用supplyAsync，需要传入一个函数表达式和线程池，有返回值，与FutureTask用法相同，可以使用get()获取返回值；
         * 5.2 whenComplete whenCompleteAsync
         * whenComplete可以感知异步执行的结果，并获取执行结束后的返回值和异常错误，这里只能捕获异常，并不能抛出异常，需要使用
         * exceptionally()方法返回异常，该方法返回的是一个Integer类型
         * whenCompleteAsync默认是异步方法，会开启一个新的线程或一个线程池
         *         CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
         *             System.out.println("当前线程ID：" + Thread.currentThread().getId());
         *             int i = 10 / 0;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService).whenComplete((t, u) -> {
         *             System.out.println("任务运行结果为：" + t + ";异常信息为：" + u);
         *         }).exceptionally(throwable -> 10);
         *
         * 5.3 handle
         * handle方法与whenComplete方法类似，也可以感知异步任务执行的结果，但handle方法可以操作异步返回的结果，并将结果进行返回
         *         CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
         *                     System.out.println("当前线程ID：" + Thread.currentThread().getId());
         *                     int i = 10 / 5;
         *                     System.out.println("线程运行结果为：" + i);
         *                     return i;
         *                 }, executorService)
         *                 .handle((res, throwable) -> {
         *                     if (res != null) {
         *                         return res * 2;
         *                     }
         *                     if (throwable != null) {
         *                         return 0;
         *                     }
         *
         *                     return 10;
         *                 });
         * 5.4 线程串行化
         * CompletableFuture<Void> thenAccept(Consumer<? super T> action)
         * CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
         * CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
         *
         * <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
         * <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
         * <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
         *
         * CompletableFuture<Void> thenRun(Runnable action)
         * CompletableFuture<Void> thenRunAsync(Runnable action)
         * CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
         *
         * 以上九种方法都可以实现线程串行化
         * thenAccept方法可以接收异步任务的返回值并处理，但自身并不能返回结果
         *         CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
         *                     System.out.println("当前线程ID：" + Thread.currentThread().getId());
         *                     int i = 10 / 5;
         *                     System.out.println("线程运行结果为：" + i);
         *                     return i;
         *                 }, executorService)
         *                 .thenApply(result -> {
         *                     return result * 2;
         *                 });
         * thenApply方法可以接收异步任务的返回值和异常，并且可以返回结果
         * thenRun方法不接收异步任务的返回值，也不返回结果，只进行下一个线程
         *
         * 5.5 同时运行
         * <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
         * CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
         * <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
         *
         * 以上三种方法都是在指定的两个任务都执行结束后才继续执行下一个任务，它们也同样有Async方法，可实现异步执行，同时可以再传入线程池，指定线程池去执行
         *
         * thenAcceptBoth方法可操作两个线程的返回值，但没有自己的返回值
         * runAfterBoth方法直接开启下一个线程，无法获取到之前线程的返回值也无法操作
         * thenCombine方法可操作两个线程的返回值，并返回自己的返回值
         *
         *         CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
         *             System.out.println("任务01已启动，当前线程ID：" + Thread.currentThread().getId());
         *             int i = 10 / 5;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         *         CompletableFuture<Integer> future02 = CompletableFuture.supplyAsync(() -> {
         *             System.out.println("任务02已启动，当前线程ID：" + Thread.currentThread().getId());
         *             int i = 100 / 2;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         *
         *         CompletableFuture<Integer> future = future01.thenCombineAsync(future02, (t, u) -> {
         *             System.out.println("任务03已启动，当前线程ID：" + Thread.currentThread().getId());
         *             int i = t + u;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         *
         *         System.out.println("业务返回值：" + future.get());
         *
         * 5.6 俩任务中任意任务完成后启动下一个任务
         * CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
         * <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
         * CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
         *
         * 以上三种方法都实现了两个任务中任意一个任务完成后，开始第三个任务，其都有Async异步方法，并且可以传入指定的线程池
         *
         * acceptEither方法可以获取第一个任务的返回值，但自身没有返回值
         * applyToEither方法可以获得第一个任务的返回值，也可以返回自己的值
         * runAfterEither方法无法获得之前任务的返回值，也无法返回自己的值
         *
         *         CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
         *             System.out.println("任务01已启动，当前线程ID：" + Thread.currentThread().getId());
         *             int i = 10 / 2;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         *         CompletableFuture<Integer> future02 = CompletableFuture.supplyAsync(() -> {
         *             System.out.println("任务02已启动，当前线程ID：" + Thread.currentThread().getId());
         *             int i = 100 / 2;
         *             try {
         *                 Thread.sleep(3000);
         *             } catch (InterruptedException e) {
         *                 e.printStackTrace();
         *             }
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         *
         *         CompletableFuture<Integer> future = future01.applyToEitherAsync(future02, t -> {
         *             System.out.println("任务03已启动，当前线程ID：" + Thread.currentThread().getId());
         *             int i = t * 2;
         *             System.out.println("线程运行结果为：" + i);
         *             return i;
         *         }, executorService);
         *
         *         System.out.println("业务返回值：" + future.get());
         *
         * 5.7 组合执行
         * CompletableFuture还提供了组合任意数量的线程的方法
         * CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
         * CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
         *
         * allOf方法会等待所有的任务都结束后再去执行
         * anyOf方法会在任意一个任务结束后就去执行，可以获得完成线程的返回值
         *
         *         CompletableFuture<Object> future = CompletableFuture.anyOf(future01, future02);
         *         System.out.println("业务返回值：" + future.get());
         *
         * 1,2两种方法无法获取返回值,3和4是可以获取到返回值
         * 1,2,3方法均无法实现资源控制,每一个线程都需要创建一个线程,消耗资源大
         */

        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务01已启动，当前线程ID：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("线程运行结果为：" + i);
            return i;
        }, executorService);
        CompletableFuture<Integer> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务02已启动，当前线程ID：" + Thread.currentThread().getId());
            int i = 100 / 2;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程运行结果为：" + i);
            return i;
        }, executorService);

        CompletableFuture<Object> future = CompletableFuture.anyOf(future01, future02);

        System.out.println("业务返回值：" + future.get());

        System.out.println("==================结束业务==================");
    }

    static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程ID：" + Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("线程运行结果为：" + i);
        }
    }

    static class Runnable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程ID：" + Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("线程运行结果为：" + i);
        }
    }

    static class Callable01 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程ID：" + Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("线程运行结果为：" + i);
            return i;
        }
    }
}
